"""
ModelForm类
"""

from app.utils.bootstrap import BootstrapModelForm # 导入表单控件的bootstrap属性样式

from django.core.validators import RegexValidator  # 进行数据校验的正则表达式
from django.core.exceptions import ValidationError  # 捕获表单提交的异常错误

from app.models import UserInfo, PrettyNumber, Admin, Order  # 数据表类

from django import forms  # 构建表单模型
from app.utils.encrypt import md5

class UserModelForm(BootstrapModelForm):
    """
     用户类表单模型
     作用：用于定义渲染到前端的数据，设置校验规则等
     用法：
     用ModelForm来操作表记录
     1.基本用法：首先从django.forms导入ModelForm；
     2.编写一个自己的类，继承ModelForm；
     3.在新类里，设置元类Meta；
     4.在Meta中，设置model属性为你要关联的ORM模型，这里是UserInfo；
     5.在Meta中，设置fields属性为你要在表单中使用的字段列表；
     6.列表里的值，应该是ORM模型model中的字段名。

     继承BootstrapModelForm类实现表单控件属性样式
    """

    # 重新定义name字段的属性和校验规则
    name = forms.CharField(min_length=2, label='姓名')

    class Meta:
        model = UserInfo  # 指定需要显示的表名（在models.py中定义）

        # 指定要操作的字段名，是一个列表
        fields = ['name', 'age', 'gender', 'depart', 'create_time', 'account']

        # 给表单控件逐个添加属性
        # widgets = {
        #     'name': forms.TextInput(attrs={'class': 'form-control'}),
        #     'password': forms.PasswordInput(attrs={'class': 'form-control'}),
        #     'create_time': forms.DateInput(attrs={'class': 'form-control'}),
        # }


class PrettyNumberForm(BootstrapModelForm):
    """
    靓号类表单模型
    具体使用注解，见UserModelForm类
    继承BootstrapModelForm类实现表单控件属性样式
    """

    # 重新定义mobile字段的属性和校验规则 方式1
    mobile = forms.CharField(
        min_length=11,  # 最小长度
        label='号码',
        # 通过正则进行数据校验
        validators=[RegexValidator(r'^1[3-9]\d{9}$', '号码错误,长度不能超过11位')],
    )

    class Meta:
        model = PrettyNumber  # 指定需要显示的表名（在models.py中定义）
        # 指定要操作的字段名，是一个列表
        # fields = ['mobile', 'price', 'level', 'status']
        fields = '__all__'  # 渲染所有字段
        # exclude = ['level'] # 排除某个字段

    # 对字段进行一些数据校验或操作 方式2
    # 名称必须是clean_字段名
    # 也称为钩子方法，更灵活，比如手机号是否已经存在...
    def clean_mobile(self):
        txt_value = self.cleaned_data['mobile']  # 获取到用户输入的字段值

        num_exist = PrettyNumber.objects.filter(mobile=txt_value).exists()
        if num_exist:
            raise ValidationError("号码已经存在")

        return txt_value


class PrettyNumberEditForm(BootstrapModelForm):
    """
    靓号编辑类表单模型
    具体使用注解，见UserModelForm类
    继承BootstrapModelForm类实现表单控件属性样式
    """

    # 重新定义mobile字段的属性和校验规则 方式1
    mobile = forms.CharField(
        label='号码',
        disabled=True,
    )

    class Meta:
        model = PrettyNumber  # 指定需要显示的表名（在models.py中定义）
        # 指定要操作的字段名，是一个列表
        # fields = ['mobile', 'price', 'level', 'status']
        fields = '__all__'  # 渲染所有字段

    # 对字段进行一些数据校验或操作 方式2
    # 名称必须是clean_字段名
    # 也称为钩子方法，更灵活，比如手机号是否已经存在...
    def clean_mobile(self):
        txt_value = self.cleaned_data['mobile']  # 获取到用户输入的字段值

        # instance是编辑时传入的值，pk是id主键
        nid = self.instance.id
        # print(self.instance.mobile)

        # 修改时，要排除当前号码以外的其他数据
        # 也就是 where mobile=18900000000 and id!=2
        num_exist = PrettyNumber.objects.filter(mobile=txt_value).exclude(id=nid)
        if num_exist:
            raise ValidationError("号码已经存在")

        return txt_value


class AdminForm(BootstrapModelForm):
    """
    管理员类表单模型
    具体使用注解，见UserModelForm类
    继承BootstrapModelForm类实现表单控件属性样式
    """
    # 再生成一个确认密码输入框
    confirm_password = forms.CharField(
        label='确认密码',
        # render_value=True 不清空密码框
        widget=forms.PasswordInput(render_value=True),
        max_length=64)

    class Meta:
        model = Admin  # 指定需要显示的表名（在models.py中定义）
        # 指定要操作的字段名，是一个列表
        # fields = ['mobile', 'price', 'level', 'status']
        fields = '__all__'  # 渲染所有字段
        widgets = {
            'password': forms.PasswordInput(render_value=True),
        }
        # exclude = ['level'] # 排除某个字段

    # 对字段进行一些数据校验或操作 方式2
    # 名称必须是clean_字段名
    # 也称为钩子方法，更灵活，比如手机号是否已经存在...
    def clean_confirm_password(self):
        password_value = self.cleaned_data['password']  # 获取到用户输入的字段值，此时密码已经md5加密了
        confirm_password_value = md5(self.cleaned_data['confirm_password'])  # 对确认框也进行md5加密

        if password_value != confirm_password_value:  # 两个密文之间进行比对
            raise ValidationError('密码两次输入不一致')
        # 返回什么就保存到数据库中什么，在这儿因为再次输入的密码不保存，所以只返回
        return confirm_password_value  # 又保存到cleaned_data中了

    def clean_password(self):
        # 对密码明文进行md5加密
        password_value = self.cleaned_data['password']  # 获取到用户输入的字段值
        return md5(password_value)

    def clean_username(self):
        # 判断输入的管理员帐户是否已经存在
        username_value = self.cleaned_data['username']
        username_exist = Admin.objects.filter(username=username_value).exists()

        if username_exist:
            raise ValidationError('用户名已存在')
        # 返回什么就保存到数据库中什么，在这儿因为再次输入的密码不保存，所以只返回
        return username_value  # 又保存到cleaned_data中了


class AdminEditForm(BootstrapModelForm):
    """
    管理员姓名修改类表单模型
    具体使用注解，见UserModelForm类
    继承BootstrapModelForm类实现表单控件属性样式
    """

    class Meta:
        model = Admin
        fields = ['username']

    # 对字段进行一些数据校验或操作 方式2
    # 名称必须是clean_字段名
    # 也称为钩子方法，更灵活，比如手机号是否已经存在...
    def clean_username(self):
        # 判断输入的管理员帐户是否已经存在
        username_value = self.cleaned_data['username']
        username_exist = Admin.objects.filter(username=username_value).exists()

        if username_exist:
            raise ValidationError('用户名已存在')
        # 返回什么就保存到数据库中什么，在这儿因为再次输入的密码不保存，所以只返回
        return username_value  # 又保存到cleaned_data中了


class AdminResetForm(BootstrapModelForm):
    """
    管理员重置密码类表单模型
    具体使用注解，见UserModelForm类
    继承BootstrapModelForm类实现表单控件属性样式
    """
    # 再生成一个确认密码输入框
    confirm_password = forms.CharField(
        label='确认密码',
        # render_value=True 不清空密码框
        widget=forms.PasswordInput(render_value=True),
        max_length=64)

    class Meta:
        model = Admin
        fields = ['password', 'confirm_password']
        widgets = {
            'password': forms.PasswordInput(render_value=True),
        }

    # 对字段进行一些数据校验或操作 方式2
    # 名称必须是clean_字段名
    # 也称为钩子方法，更灵活，比如手机号是否已经存在...
    def clean_confirm_password(self):
        password_value = self.cleaned_data.get('password')  # 获取到用户输入的字段值，此时密码已经md5加密了
        confirm_password_value = md5(self.cleaned_data.get('confirm_password'))  # 对确认框也进行md5加密

        if password_value != confirm_password_value:  # 两个密文之间进行比对
            raise ValidationError('密码两次输入不一致')
        # 返回什么就保存到数据库中什么，在这儿因为再次输入的密码不保存，所以只返回
        return confirm_password_value  # 又保存到cleaned_data中了

    def clean_password(self):
        # 对密码明文进行md5加密
        password_value = self.cleaned_data.get('password')  # 获取到用户输入的字段值
        md5_pwd = md5(password_value)

        # 去数据库检验当前新密码是否和原密码一致
        # instance是编辑时传入的值，pk是id主键
        pwd = Admin.objects.filter(id=self.instance.pk, password=md5_pwd).exists()
        if pwd:
            raise ValidationError("密码未做修改")
        return md5_pwd


class OrderModelForm(BootstrapModelForm):
    """
     订单类表单模型
     作用：用于定义渲染到前端的数据，设置校验规则等
     继承BootstrapModelForm类实现表单控件属性样式
    """
    #
    # # 重新定义name字段的属性和校验规则
    # name = forms.CharField(min_length=2, label='姓名')

    class Meta:
        model = Order  # 指定需要显示的表名
        fields = '__all__'
        exclude =['oid','user'] # 排除oid订单编号显示

